#include "UDP.h"
#ifdef _WIN32
#define INVALID_SOCKET  (SOCKET)(~0)
#include <ws2tcpip.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#else
#define INVALID_SOCKET  (int)(~0)
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdarg.h>
#endif
#define LOG_BUF_SIZE			1024
static timeval zerots;


#define SOCKET_ERROR            (-1)

UDP::UDP()
{
	static bool isInit = false;
	if (!isInit)
	{
		isInit = true;
#if _WIN32
		WSAData wd;
		if (WSAStartup(MAKEWORD(2, 2), &wd))
		{
			ErrorLog("WSAStartup error:%d", GetLastError());
			WSACleanup();
			return;
		}
#endif
	}

	memset(&zerots, 0, sizeof(timeval));
}

UDP::~UDP()
{
	Terminate();
}

bool UDP::Initialize(const char* ip, uint16_t port)
{
	Terminate();

	udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (udpSocket == INVALID_SOCKET)
	{
		ErrorLog("socket error:%d",
#if _WIN32
			GetLastError()
#else
			errno
#endif
		);
		return false;
	}

	sockaddr_in addr;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_family = AF_INET;
	int len = sizeof(sockaddr);
	if (bind(udpSocket, (sockaddr*)&addr, len) == SOCKET_ERROR)
	{
		ErrorLog("bind error:%d",
#if _WIN32
			GetLastError()
#else
			errno
#endif
		);
		return false;
	}
	int enable = 1;
	int ret = setsockopt(udpSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&enable, sizeof(enable));
	if (ret) {
		ErrorLog("setsockopt broadcast error");
		return false;
	}
#ifdef _WIN32
	int64_t timeout = 1;
	ret = setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(int64_t));
#else
	timeval tv = { 0, 1000 };
	ret = setsockopt(udpSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));
#endif

	if (ret) {
		ErrorLog("setsockopt rcvtimeo error");
		return false;
	}
	return true;
}

void UDP::Terminate()
{
	if (udpSocket != 0)
	{
#if _WIN32
		closesocket(udpSocket);
#else
		close(udpSocket);
#endif
	}
	udpSocket = 0;
}

void UDP::Update(uint32_t timeout)
{
	HandleRecv();
}

void UDP::SendTo(uint8_t* buffer, uint32_t len, sockaddr_in* remote)
{
	sendto(udpSocket, (char*)buffer, len, 0, (sockaddr*)remote, sizeof(sockaddr));
}

void UDP::SendTo(uint8_t* buffer, uint32_t len, const char* ip, const uint16_t port)
{
	sockaddr_in _add;
	sockaddr_in* addr = &_add;
	addr->sin_port = htons(port);
	addr->sin_addr.s_addr = inet_addr(ip);
	addr->sin_family = AF_INET;
	SendTo(buffer, len, addr);
}

void UDP::OnRecvMsg(uint8_t* buffer, uint32_t len, sockaddr_in* addr)
{
	if (onRecv)
		onRecv(buffer, len, addr, User);
}

void UDP::HandleRecv()
{
	while (udpSocket)
	{
		sockaddr_in addrClient;
		char buffer[65535];
#if _WIN32
		int addrlen = sizeof(sockaddr_in);
		int len = recvfrom(udpSocket, buffer, sizeof(buffer), 0, (sockaddr*)&addrClient, &addrlen);
#else
		socklen_t addrlen = sizeof(sockaddr_in);
		int len = recvfrom(udpSocket, buffer, sizeof(buffer), 0, (sockaddr*)&addrClient, &addrlen);
#endif
		if (len <= 0)
			break;
		buffer[len] = 0;
		OnRecvMsg((uint8_t*)buffer, len, &addrClient);
	}
}


void UDP::ErrorLog(const char* _Format, ...)
{
	va_list ap;
	char buf[LOG_BUF_SIZE] = { 0 };
	va_start(ap, _Format);
	vsnprintf(buf, LOG_BUF_SIZE, _Format, ap);
	va_end(ap);

	if (onErrorLog)
		onErrorLog(buf, User);
	else
		printf("%s\n", buf);
}

int64_t UDP::GetTimestamp()
{
#if _WIN32
	FILETIME ft;
	int64_t t;
	GetSystemTimeAsFileTime(&ft);
	t = (int64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
	return t / 10 - 11644473600000000;
#else
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
#endif
}